Shaun Mccran

My digital playground

22
D
E
C
2009

Using Isapi / Apache rewriting to mask URL strings, for cosmetics and security

One of the more recent additions to my Coldfusion frameworks is masking the more ugly URL's using Isapi rewrite. In this article I'll be using Helicon's Isapi ReWrite, but Apache re write works in much the same way.

Usually in your Coldfusion frameworks, most other technologies as well, you are passing around a variable or two to control the page content, and more often than not it is in the url. It never looks particularly clean if your URL has a long name value query string behind it, like this:

view plain print about
1http://www.mysite.com/index.cfm?variable1=pagename&location=england&value=7

Cosmetic reasons

So for two reasons URL rewriting seems like a good idea.

Firstly to mask those ugly URLS with a url rewriter. On a basic level this will re write specified request to the URL you tell it to, taking your ugly list of name value pairs and changing it into a user friend URL. If you are pitching this to a client this looks a lot more professional.

Security reasons

Secondly there is an added security benefit here. The URL gives a lot away about a website, like what the code base is, and is potentially a window on the internal workings of a website. Take a normal FuseBox application for example. The normal URL might be:

view plain print about
1www.mysite.com/index.cfm?fuseaction=controller.action&othervalues=values

From here it is very easy to start messing around with the controller names, trying to dig out an 'admin' controller, or other common function controller. Similarly adding values to pages where it is obvious a Query has been fired is an easy way of testing of the developer is using 'cfQueryParam', with potentially disastrous results.

Along the same lines it is quite simple to inject form values into the URL (like this http://www.mccran.co.uk/index.cfm/2009/7/30/Cross-site-Script-hacking-using-the-GET-method). By masking the URL and the values you make it considerably more difficult to do this, after all if you can see or get to the URL, how can you fool around with it?

So far I am implementing a rewrite script that will rewrite URLs into friendly strings, here is a modified version of the .htaccess file I'm using.

view plain print about
1# Helicon ISAPI_Rewrite configuration file
2# Version 3.1.0.68
3
4RewriteEngine on
5RewriteBase /wwwroot/
6
7#generic
8RewriteRule requestID/(.*)/(.*)/ index.cfm?decryptURL=$1&params=$2
9
10# site pages
11RewriteRule home(/)? index.cfm?go=controller.home
12RewriteRule contact(/)? index.cfm?go=controller.contact
13RewriteRule login(/)? index.cfm?go=controller.login
14RewriteRule privacy(/)? index.cfm?go=controller.privacy
15RewriteRule about(/)? index.cfm?go=controller.about
16RewriteRule faqs(/)? index.cfm?go=controller.faqs
17RewriteRule search(/)? index.cfm?go=controller.search

This code starts off by turning the rewriteEngine on, then setting the rewriteBase, this is typically your webroot, or the root of the site the file is for. Then it rewrites any URL params to the URL string.

The main part of the code is where we set individual rewriteRule's for each URL. The first example (home) looks for any URL requests to the 'home' string, and re writes this to the URL in the regular expression (index.cfm?go=controller.home). Pretty straight forward really.

There is a lot more you can do with this, and hopefully I'll get to explore rewriting in more depth in the future.

16
D
E
C
2009

Using Isapi rewrite to serve up non existing templates

I was discussing some ideas for an application framework this morning with the team, and one of the issues we hit upon was having a common directory for templates, but serving them up as if they were from a different directory.

The idea is to have one instance of a reusable skinnable template, that appears to live on several sites.

IE all the content lives in "webroot/content/templateName.cfm", but is actually served up by many sites, IE "127.0.0.1/site1/template1.cfm", "127.0.0.1/site2/template1.cfm" ... etc

In this way they can be re skinned or adapted as needed, and they aren't database driven. The main stumbling block for the discussion was the need to actually create blank versions of each of the named templates, in each of the sites, as ColdFusion server would error on the request.

I spent twenty minutes trying to work it so that my Application.cfc's onRequest or onRequestStart method would intercept the request before it was actually made, but that just wasn't working. My other idea was to use the onMissingTemplate method, but the server is only running ColdFusion 7, so that was a no go (I figured I could catch the missing template request and just re path it, although I'd have to assess if that was really inefficient due to almost every page request logging as failed).

My eventual solution was Isapi rewrite. I am re writing all the requests to the same template, and just passing in the template variable. In that way I can request pages that don't actually exist, but they appear in the url.

Create an index.cfm template like this:

view plain print about
1<h1>I am the index page</h1>
2<ul>
3    <li><a href="page1">Page 1</a></li>
4    <li><a href="page2">Page 2</a></li>
5    <li><a href="page3">Page 3</a></li>
6    <li><a href="page4">Page 4</a></li>
7</ul>
8
9<cfdump var="#url#">
10<!--- write a handler to go get the url var passed in --->

For this example I am using the free version of Helicon's Isapi rewrite, you can get it here: Link to Helicons Isapi re write

In the example below I have altered the first page link to look like it is actually a .cfm template request, just in case you want the url string to have a .fileextension look to it.

view plain print about
1# Helicon ISAPI_Rewrite configuration file
2# Version 3.1.0.68
3
4RewriteEngine on
5RewriteBase /mywebroot
6
7#no physical page testing
8RewriteRule page1.cfm(/)? isapitest/index.cfm?p=page1
9RewriteRule page2(/)? /index.cfm?p=page2
10RewriteRule page3(/)? /index.cfm?p=page3
11RewriteRule page4(/)? /index.cfm?p=page4

So when you fire it up and test it you just see /page1, /page2 etc, and the pages don't actually exist.

I'm not experienced enough with Isapi rewrite to know if there is a downside to this, but bookmarking in a browser still works correctly, so I can't see an issues at present.

20
N
O
V
2009

Exploring the variations in email UN subscription methods

If you are anything like me, everyday you are smothered in a stack of emails from various companies all delivering html formatted, image heavy flyers persuading you to buy whatever dead horse they are flogging. I know it is my own fault, after all I signed up to them in the first place, I've only got myself to blame. As I find myself checking (and deleting) these email on an Android mobile device now it has become hassle-some. So I set out to unsubscribe from the majority of them, which is where the inspiration for this article came from.

In a short period of time I have unsubscribed from around a dozen different email newsletters, and because of this the differences in the methods used to accomplish this have become glaringly obvious. I was very surprised at the variations in the methods used, some are very user friendly, and others are very much not. I won't name the companies involved.

Case one:

By far the easiest user interaction, this is a simple link from an email that provides you with a screen confirming your UN subscription. No prompt is required from the user, you are simply unsubscribed. All the data is passed seamlessly behind the scenes.

Case two:

Almost the same as above except that you are taken to a screen where you have to commit an action. You are presented with a screen displaying your email address, and prompted to click a button to confirm the UN subscribe action. This may be a handy safety net to step errors from the casual clicker.

Case three:

Clicking on the link from an email takes you to a generic page with a form on it. They have not transferred any form of token from the email so you are prompted to enter the email address you want to UN subscribe. Entering your email address takes you to a screen confirming your action. This additional step is unnecessary, and doesn't add any value at all.

Case four:

In this scenario we are passed to a page where we have to login. The subscription details are attached to an account application, and as such your credentials need to be verified. Based on passing an encrypted token from the email a user could easily be logged in automatically. Actually requiring a username and password adds an additional level of complexity. Once I logged in I then had to navigate to the subscription settings and choose to unsubscribe with a form similar to the ones mentioned above.

Case five:

In the most convoluted example I have come across so far I actually had to create an account. I arrived at the UN subscribe page and found that to subscribe did not require an account, but to change my subscription options did! This was a very strange scenario, requiring a user to create an account to stop emails. Once you have created an account the subscription options were managed through a form like the ones mention above.

Conclusions

I think the thing to keep in mind with functionality like this is the balance between usability and the business objectives. An obvious point to note is that businesses do not want their users to UN subscribe to their emails. This is their user base and they do not want to lose it. Still it is worth considering just how you interact with your customers, even when providing functionality you do not want them to use.

After all you can always just 'Mark as Spam'.

07
N
O
V
2009

Testing methodologies - Regression testing

One of the more overlooked forms of testing (you do test don't you?) is regression testing. I'm a big fan of scripted testing using both scripted tests to actually run against your code base (think cfUnit or Junit) and scripted testing as in a basic word doc of testing instructions.

This word doc can be as simple as 'click button N' - what displayed on screen? You can literally just list the actions, expected consequences and actual conqequences.

Regression testing is the practice of going back after a release and testing the functionality that was already present. IE did you break anything by releasing your new functionality. Often the business and IT focus is on the shiny new development, not the integrity of the existing application.

Developers in particular are guilty of zoning in on the specifc area that they are directly involved with. This can sometimes lead to other areas suffering, especially if you have an OO application layer. In just how many places is each individual object referenced? A change to it may work in one area, but have devastating consequences in another.

I've seen cases of this where its been months later before an error has reared its head, and without an accurate change log it can be difficult to track the root cause down. Needless error tracking and bug fixes take developers away from actually developing, and essentially cost the business money due to bad practice.

I mentioned scripted testing above as it has had unforseen beneficial consequences. If you have done anything like this in the past, your regression testing will be very easy. You will have a handy library of repeatable scripted tests, so it is very easy for you to measure the previous results against any new tests you might perform. Thus making it instantly obvious wether your functionality is still behaving as it was before the release.

_UNKNOWNTRANSLATION_ /